import 'dart:io';
import 'dart:async';
import 'package:camera/camera.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart';

class RecordingService {
  RecordingService._internal();
  static final RecordingService instance = RecordingService._internal();

  CameraController? _controller;
  List<CameraDescription> _cameras = const [];
  final ValueNotifier<bool> recording = ValueNotifier<bool>(false);
  bool _initialized = false;
  String? _targetPath;
  DeviceOrientation? _lockedOrientation;
  Timer? _chunkTimer;
  final Duration _chunkDuration = const Duration(minutes: 10);
  bool _serviceActive = false;
  String? _videoDirPath;
  String? _currentSegmentLabel;

  CameraController? get controller => _controller;
  bool get isInitialized =>
      _initialized && (_controller?.value.isInitialized ?? false);

  Future<void> ensureInitialized(List<CameraDescription> cameras) async {
    if (_initialized &&
        _controller != null &&
        _controller!.value.isInitialized) {
      return;
    }
    _cameras = cameras;
    if (_cameras.isEmpty) {
      throw Exception('no camera');
    }
    final statuses = await [
      Permission.camera,
      Permission.microphone,
      Permission.storage,
    ].request();
    if (!(statuses[Permission.camera]!.isGranted &&
        statuses[Permission.microphone]!.isGranted)) {
      throw Exception('permissions not granted');
    }
    _controller = CameraController(
      _cameras[0],
      ResolutionPreset.high,
      enableAudio: true,
    );
    await _controller!.initialize();
    _initialized = true;
  }

  Future<void> startRecording() async {
    if (_controller == null || !_controller!.value.isInitialized) {
      throw Exception('controller not initialized');
    }
    if (_controller!.value.isRecordingVideo) {
      return;
    }
    final appDir = await getApplicationDocumentsDirectory();
    _videoDirPath = '${appDir.path}/Videos';
    await Directory(_videoDirPath!).create(recursive: true);
    _serviceActive = true;
    await _startNewSegment();
    _scheduleRotation();
  }

  Future<String?> stopRecording() async {
    if (_controller == null || !_controller!.value.isRecordingVideo) {
      return null;
    }
    _serviceActive = false;
    _chunkTimer?.cancel();
    _chunkTimer = null;
    final xfile = await _controller!.stopVideoRecording();
    String? savedPath;
    if (_targetPath != null) {
      final recordedFile = File(xfile.path);
      final targetFile = File(_targetPath!);
      if (await recordedFile.exists()) {
        await recordedFile.copy(targetFile.path);
        await recordedFile.delete();
        savedPath = _targetPath;
      } else {
        savedPath = xfile.path;
      }
    } else {
      savedPath = xfile.path;
    }
    recording.value = false;
    _targetPath = null;
    return savedPath;
  }

  Future<void> setOrientation(DeviceOrientation? orientation) async {
    if (_controller == null) return;
    if (orientation == null) {
      await _controller!.unlockCaptureOrientation();
      _lockedOrientation = null;
    } else {
      await _controller!.lockCaptureOrientation(orientation);
      _lockedOrientation = orientation;
    }
  }

  Future<void> dispose() async {
    try {
      if (_controller != null) {
        await _controller!.dispose();
      }
    } finally {
      _controller = null;
      _initialized = false;
      recording.value = false;
      _targetPath = null;
      _lockedOrientation = null;
      _chunkTimer?.cancel();
      _chunkTimer = null;
      _serviceActive = false;
      _videoDirPath = null;
    }
  }

  Future<void> _startNewSegment() async {
    if (_controller == null) return;
    final currentTime = DateTime.now().millisecondsSinceEpoch.toString();
    _targetPath = '$_videoDirPath/video_$currentTime.mp4';
    _currentSegmentLabel = _formatDateTime(DateTime.now());
    await _controller!.startVideoRecording();
    recording.value = true;
  }

  Future<String?> _stopCurrentSegment() async {
    if (_controller == null || !_controller!.value.isRecordingVideo) {
      return null;
    }
    final xfile = await _controller!.stopVideoRecording();
    String? savedPath;
    if (_targetPath != null) {
      final recordedFile = File(xfile.path);
      final targetFile = File(_targetPath!);
      if (await recordedFile.exists()) {
        await recordedFile.copy(targetFile.path);
        await recordedFile.delete();
        savedPath = _targetPath;
      } else {
        savedPath = xfile.path;
      }
    } else {
      savedPath = xfile.path;
    }
    return savedPath;
  }

  void _scheduleRotation() {
    _chunkTimer?.cancel();
    _chunkTimer = Timer(_chunkDuration, () async {
      if (!_serviceActive) {
        return;
      }
      if (_controller == null || !_controller!.value.isInitialized) {
        _scheduleRotation();
        return;
      }
      String? labelForStopped;
      String? savedPath;
      if (_controller!.value.isRecordingVideo) {
        labelForStopped = _currentSegmentLabel;
        savedPath = await _stopCurrentSegment();
      }
      await _startNewSegment();
      if (savedPath != null && labelForStopped != null) {
        _applyWatermark(savedPath, labelForStopped);
      }
      _scheduleRotation();
    });
  }

  String _formatDateTime(DateTime dt) {
    final y = dt.year.toString().padLeft(4, '0');
    final m = dt.month.toString().padLeft(2, '0');
    final d = dt.day.toString().padLeft(2, '0');
    final hh = dt.hour.toString().padLeft(2, '0');
    final mm = dt.minute.toString().padLeft(2, '0');
    final ss = dt.second.toString().padLeft(2, '0');
    return "$y/$m/$d $hh:$mm:$ss";
  }

  Future<void> _applyWatermark(String inputPath, String label) async {
    try {
      final dir = File(inputPath).parent;
      final tmpPath = '${dir.path}/wm_${DateTime.now().millisecondsSinceEpoch}.mp4';
      String fontOpt = '';
      final roboto = File('/system/fonts/Roboto-Regular.ttf');
      if (roboto.existsSync()) {
        fontOpt = ":fontfile='/system/fonts/Roboto-Regular.ttf'";
      }
      final safeLabel = label.replaceAll("'", "\u2019");
      final cmd = "-y -i '$inputPath' -vf drawtext=text='$safeLabel'${fontOpt}:fontcolor=white:fontsize=24:x=10:y=10:box=1:boxcolor=black@0.4:boxborderw=6 -c:a copy '$tmpPath'";
      await FFmpegKit.execute(cmd);
      final tmpFile = File(tmpPath);
      if (await tmpFile.exists()) {
        final orig = File(inputPath);
        await orig.delete();
        await tmpFile.rename(inputPath);
      }
    } catch (_) {}
  }
}
